<!DOCTYPE HTML>
<html lang="zh-Hans" class="light" dir="ltr">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>TypeScript 5.0 - TypeScript 使用指南手册</title>


        <!-- Custom HTML head -->
        
        <meta name="description" content="TypeScript Handbook 中文翻译。">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff">

        <link rel="icon" href="../../favicon.svg">
        <link rel="shortcut icon" href="../../favicon.png">
        <link rel="stylesheet" href="../../css/variables.css">
        <link rel="stylesheet" href="../../css/general.css">
        <link rel="stylesheet" href="../../css/chrome.css">
        <link rel="stylesheet" href="../../css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../../highlight.css">
        <link rel="stylesheet" href="../../tomorrow-night.css">
        <link rel="stylesheet" href="../../ayu-highlight.css">

        <!-- Custom theme stylesheets -->

    </head>
    <body class="sidebar-visible no-js">
    <div id="body-container">
        <!-- Provide site root to javascript -->
        <script>
            var path_to_root = "../../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script>
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script>
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('light')
            html.classList.add(theme);
            var body = document.querySelector('body');
            body.classList.remove('no-js')
            body.classList.add('js');
        </script>

        <input type="checkbox" id="sidebar-toggle-anchor" class="hidden">

        <!-- Hide / unhide sidebar before it is displayed -->
        <script>
            var body = document.querySelector('body');
            var sidebar = null;
            var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            } else {
                sidebar = 'hidden';
            }
            sidebar_toggle.checked = sidebar === 'visible';
            body.classList.remove('sidebar-visible');
            body.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded affix "><a href="../../PREFACE.html">前言</a></li><li class="chapter-item expanded affix "><li class="part-title">快速上手</li><li class="chapter-item expanded "><a href="../../zh/tutorials/index.html"><strong aria-hidden="true">1.</strong> 快速上手</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../zh/tutorials/typescript-in-5-minutes.html"><strong aria-hidden="true">1.1.</strong> 5 分钟了解 TypeScript</a></li><li class="chapter-item expanded "><a href="../../zh/tutorials/asp.net-core.html"><strong aria-hidden="true">1.2.</strong> ASP.NET Core</a></li><li class="chapter-item expanded "><a href="../../zh/tutorials/asp.net-4.html"><strong aria-hidden="true">1.3.</strong> ASP.NET 4</a></li><li class="chapter-item expanded "><a href="../../zh/tutorials/gulp.html"><strong aria-hidden="true">1.4.</strong> Gulp</a></li><li class="chapter-item expanded "><a href="../../zh/tutorials/knockout.html"><strong aria-hidden="true">1.5.</strong> Knockout.js</a></li><li class="chapter-item expanded "><a href="../../zh/tutorials/react-and-webpack.html"><strong aria-hidden="true">1.6.</strong> React 与 webpack</a></li><li class="chapter-item expanded "><a href="../../zh/tutorials/react.html"><strong aria-hidden="true">1.7.</strong> React</a></li><li class="chapter-item expanded "><a href="../../zh/tutorials/angular-2.html"><strong aria-hidden="true">1.8.</strong> Angular 2</a></li><li class="chapter-item expanded "><a href="../../zh/tutorials/migrating-from-javascript.html"><strong aria-hidden="true">1.9.</strong> 从 JavaScript 迁移到 TypeScript</a></li></ol></li><li class="chapter-item expanded "><li class="part-title">手册</li><li class="chapter-item expanded "><a href="../../zh/handbook/index.html"><strong aria-hidden="true">2.</strong> 手册</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../zh/handbook/basic-types.html"><strong aria-hidden="true">2.1.</strong> 基础类型</a></li><li class="chapter-item expanded "><a href="../../zh/handbook/interfaces.html"><strong aria-hidden="true">2.2.</strong> 接口</a></li><li class="chapter-item expanded "><a href="../../zh/handbook/functions.html"><strong aria-hidden="true">2.3.</strong> 函数</a></li><li class="chapter-item expanded "><a href="../../zh/handbook/literal-types.html"><strong aria-hidden="true">2.4.</strong> 字面量类型</a></li><li class="chapter-item expanded "><a href="../../zh/handbook/unions-and-intersections.html"><strong aria-hidden="true">2.5.</strong> 联合类型和交叉类型</a></li><li class="chapter-item expanded "><a href="../../zh/handbook/classes.html"><strong aria-hidden="true">2.6.</strong> 类</a></li><li class="chapter-item expanded "><a href="../../zh/handbook/enums.html"><strong aria-hidden="true">2.7.</strong> 枚举</a></li><li class="chapter-item expanded "><a href="../../zh/handbook/generics.html"><strong aria-hidden="true">2.8.</strong> 泛型</a></li></ol></li><li class="chapter-item expanded "><li class="part-title">手册（进阶）</li><li class="chapter-item expanded "><a href="../../zh/reference/index.html"><strong aria-hidden="true">3.</strong> 手册（进阶）</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../zh/reference/advanced-types.html"><strong aria-hidden="true">3.1.</strong> 高级类型</a></li><li class="chapter-item expanded "><a href="../../zh/reference/utility-types.html"><strong aria-hidden="true">3.2.</strong> 实用工具类型</a></li><li class="chapter-item expanded "><a href="../../zh/reference/decorators.html"><strong aria-hidden="true">3.3.</strong> Decorators</a></li><li class="chapter-item expanded "><a href="../../zh/reference/declaration-merging.html"><strong aria-hidden="true">3.4.</strong> 声明合并</a></li><li class="chapter-item expanded "><a href="../../zh/reference/iterators-and-generators.html"><strong aria-hidden="true">3.5.</strong> Iterators 和 Generators</a></li><li class="chapter-item expanded "><a href="../../zh/reference/jsx.html"><strong aria-hidden="true">3.6.</strong> JSX</a></li><li class="chapter-item expanded "><a href="../../zh/reference/mixins.html"><strong aria-hidden="true">3.7.</strong> 混入</a></li><li class="chapter-item expanded "><a href="../../zh/reference/modules.html"><strong aria-hidden="true">3.8.</strong> 模块</a></li><li class="chapter-item expanded "><a href="../../zh/reference/module-resolution.html"><strong aria-hidden="true">3.9.</strong> 模块解析</a></li><li class="chapter-item expanded "><a href="../../zh/reference/namespaces.html"><strong aria-hidden="true">3.10.</strong> 命名空间</a></li><li class="chapter-item expanded "><a href="../../zh/reference/namespaces-and-modules.html"><strong aria-hidden="true">3.11.</strong> 命名空间和模块</a></li><li class="chapter-item expanded "><a href="../../zh/reference/symbols.html"><strong aria-hidden="true">3.12.</strong> Symbols</a></li><li class="chapter-item expanded "><a href="../../zh/reference/triple-slash-directives.html"><strong aria-hidden="true">3.13.</strong> 三斜线指令</a></li><li class="chapter-item expanded "><a href="../../zh/reference/type-compatibility.html"><strong aria-hidden="true">3.14.</strong> 类型兼容性</a></li><li class="chapter-item expanded "><a href="../../zh/reference/type-inference.html"><strong aria-hidden="true">3.15.</strong> 类型推论</a></li><li class="chapter-item expanded "><a href="../../zh/reference/variable-declarations.html"><strong aria-hidden="true">3.16.</strong> 变量声明</a></li></ol></li><li class="chapter-item expanded "><li class="part-title">手册（v2）</li><li class="chapter-item expanded "><a href="../../zh/handbook-v2/index.html"><strong aria-hidden="true">4.</strong> 手册（v2）</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../zh/handbook-v2/type-manipulation/template-literal-types.html"><strong aria-hidden="true">4.1.</strong> 模版字面量类型</a></li></ol></li><li class="chapter-item expanded "><li class="part-title">TypeScript 声明文件（.d.ts）</li><li class="chapter-item expanded "><a href="../../zh/declaration-files/index.html"><strong aria-hidden="true">5.</strong> 如何书写声明文件</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../zh/declaration-files/introduction.html"><strong aria-hidden="true">5.1.</strong> 介绍</a></li><li class="chapter-item expanded "><a href="../../zh/declaration-files/by-example.html"><strong aria-hidden="true">5.2.</strong> 举例</a></li><li class="chapter-item expanded "><a href="../../zh/declaration-files/library-structures.html"><strong aria-hidden="true">5.3.</strong> 库结构</a></li><li class="chapter-item expanded "><a href="../../zh/declaration-files/templates.html"><strong aria-hidden="true">5.4.</strong> 模板</a></li><li class="chapter-item expanded "><a href="../../zh/declaration-files/do-s-and-don-ts.html"><strong aria-hidden="true">5.5.</strong> 最佳实践</a></li><li class="chapter-item expanded "><a href="../../zh/declaration-files/deep-dive.html"><strong aria-hidden="true">5.6.</strong> 深入</a></li><li class="chapter-item expanded "><a href="../../zh/declaration-files/publishing.html"><strong aria-hidden="true">5.7.</strong> 发布</a></li><li class="chapter-item expanded "><a href="../../zh/declaration-files/consumption.html"><strong aria-hidden="true">5.8.</strong> 使用</a></li></ol></li><li class="chapter-item expanded "><li class="part-title">TypeScript for JavaScript</li><li class="chapter-item expanded "><a href="../../zh/javascript/type-checking-javascript-files.html"><strong aria-hidden="true">6.</strong> JavaScript 文件里的类型检查</a></li><li class="chapter-item expanded affix "><li class="part-title">工程配置</li><li class="chapter-item expanded "><a href="../../zh/project-config/index.html"><strong aria-hidden="true">7.</strong> 工程配置</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../zh/project-config/tsconfig.json.html"><strong aria-hidden="true">7.1.</strong> tsconfig.json</a></li><li class="chapter-item expanded "><a href="../../zh/project-config/project-references.html"><strong aria-hidden="true">7.2.</strong> 工程引用</a></li><li class="chapter-item expanded "><a href="../../zh/project-config/typings-for-npm-packages.html"><strong aria-hidden="true">7.3.</strong> NPM 包的类型</a></li><li class="chapter-item expanded "><a href="../../zh/project-config/compiler-options.html"><strong aria-hidden="true">7.4.</strong> 编译选项</a></li><li class="chapter-item expanded "><a href="../../zh/project-config/configuring-watch.html"><strong aria-hidden="true">7.5.</strong> 配置 Watch</a></li><li class="chapter-item expanded "><a href="../../zh/project-config/compiler-options-in-msbuild.html"><strong aria-hidden="true">7.6.</strong> 在 MSBuild 里使用编译选项</a></li><li class="chapter-item expanded "><a href="../../zh/project-config/integrating-with-build-tools.html"><strong aria-hidden="true">7.7.</strong> 与其它构建工具整合</a></li><li class="chapter-item expanded "><a href="../../zh/project-config/nightly-builds.html"><strong aria-hidden="true">7.8.</strong> 使用 TypeScript 的每日构建版本</a></li></ol></li><li class="chapter-item expanded "><li class="part-title">版本发布说明（Release Notes）</li><li class="chapter-item expanded "><a href="../../zh/release-notes/index.html"><strong aria-hidden="true">8.</strong> 新增功能</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-5.4.html"><strong aria-hidden="true">8.1.</strong> TypeScript 5.4</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-5.3.html"><strong aria-hidden="true">8.2.</strong> TypeScript 5.3</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-5.2.html"><strong aria-hidden="true">8.3.</strong> TypeScript 5.2</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-5.1.html"><strong aria-hidden="true">8.4.</strong> TypeScript 5.1</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-5.0.html" class="active"><strong aria-hidden="true">8.5.</strong> TypeScript 5.0</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.9.html"><strong aria-hidden="true">8.6.</strong> TypeScript 4.9</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.8.html"><strong aria-hidden="true">8.7.</strong> TypeScript 4.8</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.7.html"><strong aria-hidden="true">8.8.</strong> TypeScript 4.7</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.6.html"><strong aria-hidden="true">8.9.</strong> TypeScript 4.6</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.5.html"><strong aria-hidden="true">8.10.</strong> TypeScript 4.5</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.4.html"><strong aria-hidden="true">8.11.</strong> TypeScript 4.4</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.3.html"><strong aria-hidden="true">8.12.</strong> TypeScript 4.3</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.2.html"><strong aria-hidden="true">8.13.</strong> TypeScript 4.2</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.1.html"><strong aria-hidden="true">8.14.</strong> TypeScript 4.1</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-4.0.html"><strong aria-hidden="true">8.15.</strong> TypeScript 4.0</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.9.html"><strong aria-hidden="true">8.16.</strong> TypeScript 3.9</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.8.html"><strong aria-hidden="true">8.17.</strong> TypeScript 3.8</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.7.html"><strong aria-hidden="true">8.18.</strong> TypeScript 3.7</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.6.html"><strong aria-hidden="true">8.19.</strong> TypeScript 3.6</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.5.html"><strong aria-hidden="true">8.20.</strong> TypeScript 3.5</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.4.html"><strong aria-hidden="true">8.21.</strong> TypeScript 3.4</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.3.html"><strong aria-hidden="true">8.22.</strong> TypeScript 3.3</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.2.html"><strong aria-hidden="true">8.23.</strong> TypeScript 3.2</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.1.html"><strong aria-hidden="true">8.24.</strong> TypeScript 3.1</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-3.0.html"><strong aria-hidden="true">8.25.</strong> TypeScript 3.0</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.9.html"><strong aria-hidden="true">8.26.</strong> TypeScript 2.9</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.8.html"><strong aria-hidden="true">8.27.</strong> TypeScript 2.8</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.7.html"><strong aria-hidden="true">8.28.</strong> TypeScript 2.7</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.6.html"><strong aria-hidden="true">8.29.</strong> TypeScript 2.6</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.5.html"><strong aria-hidden="true">8.30.</strong> TypeScript 2.5</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.4.html"><strong aria-hidden="true">8.31.</strong> TypeScript 2.4</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.3.html"><strong aria-hidden="true">8.32.</strong> TypeScript 2.3</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.2.html"><strong aria-hidden="true">8.33.</strong> TypeScript 2.2</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.1.html"><strong aria-hidden="true">8.34.</strong> TypeScript 2.1</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-2.0.html"><strong aria-hidden="true">8.35.</strong> TypeScript 2.0</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-1.8.html"><strong aria-hidden="true">8.36.</strong> TypeScript 1.8</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-1.7.html"><strong aria-hidden="true">8.37.</strong> TypeScript 1.7</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-1.6.html"><strong aria-hidden="true">8.38.</strong> TypeScript 1.6</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-1.5.html"><strong aria-hidden="true">8.39.</strong> TypeScript 1.5</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-1.4.html"><strong aria-hidden="true">8.40.</strong> TypeScript 1.4</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-1.3.html"><strong aria-hidden="true">8.41.</strong> TypeScript 1.3</a></li><li class="chapter-item expanded "><a href="../../zh/release-notes/typescript-1.1.html"><strong aria-hidden="true">8.42.</strong> TypeScript 1.1</a></li></ol></li><li class="chapter-item expanded "><li class="part-title">破坏性改动（Breaking Changes）</li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/index.html"><strong aria-hidden="true">9.</strong> Breaking Changes</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-3.6.html"><strong aria-hidden="true">9.1.</strong> TypeScript 3.6</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-3.5.html"><strong aria-hidden="true">9.2.</strong> TypeScript 3.5</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-3.4.html"><strong aria-hidden="true">9.3.</strong> TypeScript 3.4</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-3.2.html"><strong aria-hidden="true">9.4.</strong> TypeScript 3.2</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-3.1.html"><strong aria-hidden="true">9.5.</strong> TypeScript 3.1</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-3.0.html"><strong aria-hidden="true">9.6.</strong> TypeScript 3.0</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-2.9.html"><strong aria-hidden="true">9.7.</strong> TypeScript 2.9</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-2.8.html"><strong aria-hidden="true">9.8.</strong> TypeScript 2.8</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-2.7.html"><strong aria-hidden="true">9.9.</strong> TypeScript 2.7</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-2.6.html"><strong aria-hidden="true">9.10.</strong> TypeScript 2.6</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-2.4.html"><strong aria-hidden="true">9.11.</strong> TypeScript 2.4</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-2.3.html"><strong aria-hidden="true">9.12.</strong> TypeScript 2.3</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-2.2.html"><strong aria-hidden="true">9.13.</strong> TypeScript 2.2</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-2.1.html"><strong aria-hidden="true">9.14.</strong> TypeScript 2.1</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-2.0.html"><strong aria-hidden="true">9.15.</strong> TypeScript 2.0</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-1.8.html"><strong aria-hidden="true">9.16.</strong> TypeScript 1.8</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-1.7.html"><strong aria-hidden="true">9.17.</strong> TypeScript 1.7</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-1.6.html"><strong aria-hidden="true">9.18.</strong> TypeScript 1.6</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-1.5.html"><strong aria-hidden="true">9.19.</strong> TypeScript 1.5</a></li><li class="chapter-item expanded "><a href="../../zh/breaking-changes/typescript-1.4.html"><strong aria-hidden="true">9.20.</strong> TypeScript 1.4</a></li></ol></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle">
                <div class="sidebar-resize-indicator"></div>
            </div>
        </nav>

        <!-- Track and set sidebar scroll position -->
        <script>
            var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
            sidebarScrollbox.addEventListener('click', function(e) {
                if (e.target.tagName === 'A') {
                    sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
                }
            }, { passive: true });
            var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
            sessionStorage.removeItem('sidebar-scroll');
            if (sidebarScrollTop) {
                // preserve sidebar scroll position when navigating via links within sidebar
                sidebarScrollbox.scrollTop = sidebarScrollTop;
            } else {
                // scroll sidebar to current active section when navigating via "next/previous chapter" buttons
                var activeSection = document.querySelector('#sidebar .active');
                if (activeSection) {
                    activeSection.scrollIntoView({ block: 'center' });
                }
            }
        </script>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky">
                    <div class="left-buttons">
                        <label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </label>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">TypeScript 使用指南手册</h1>

                    <div class="right-buttons">
                        <a href="../../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script>
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="typescript-50"><a class="header" href="#typescript-50">TypeScript 5.0</a></h1>
<h2 id="装饰器-decorators"><a class="header" href="#装饰器-decorators">装饰器 Decorators</a></h2>
<p>装饰器是即将到来的 ECMAScript 特性，它允许我们定制可重用的类以及类成员。</p>
<p>考虑如下的代码：</p>
<pre><code class="language-ts">class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

const p = new Person('Ron');
p.greet();
</code></pre>
<p>这里的 <code>greet</code> 很简单，但我们假设它很复杂 - 例如包含异步的逻辑，是递归的，具有副作用等。
不管你把它想像成多么混乱复杂，现在我们想插入一些 <code>console.log</code> 语句来调试 <code>greet</code>。</p>
<pre><code class="language-ts">class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  greet() {
    console.log('LOG: Entering method.');

    console.log(`Hello, my name is ${this.name}.`);

    console.log('LOG: Exiting method.');
  }
}
</code></pre>
<p>这个做法太常见了。
如果有种办法能给每一个类方法都添加打印功能就太好了！</p>
<p>这就是装饰器的用武之地。
让我们编写一个函数 <code>loggedMethod</code>：</p>
<pre><code class="language-ts">function loggedMethod(originalMethod: any, _context: any) {
  function replacementMethod(this: any, ...args: any[]) {
    console.log('LOG: Entering method.');
    const result = originalMethod.call(this, ...args);
    console.log('LOG: Exiting method.');
    return result;
  }

  return replacementMethod;
}
</code></pre>
<p>"这些 <code>any</code> 是怎么回事？都啥啊？"</p>
<p>先别急 - 这里我们是想简化一下问题，将注意力集中在函数的功能上。
注意一下 <code>loggedMethod</code> 接收原方法（<code>originalMethod</code>）作为参数并返回一个函数：</p>
<ol>
<li>打印 <code>"Entering…"</code> 消息</li>
<li>将 <code>this</code> 值以及所有的参数传递给原方法</li>
<li>打印 <code>"Exiting..."</code> 消息，并且</li>
<li>返回原方法的返回值。</li>
</ol>
<p>现在可以使用 <code>loggedMethod</code> 来<em>装饰</em> <code>greet</code> 方法：</p>
<pre><code class="language-ts">class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  @loggedMethod
  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

const p = new Person('Ron');
p.greet();

// 输出:
//
//   LOG: Entering method.
//   Hello, my name is Ron.
//   LOG: Exiting method.
</code></pre>
<p>我们刚刚在 <code>greet</code> 上使用了 <code>loggedMethod</code> 装饰器 - 注意一下写法 <code>@loggedMethod</code>。
这样做之后，<code>loggedMethod</code> 被调用时会传入被装饰的目标 <code>target</code> 以及一个上下文对象 <code>context object</code> 作为参数。
因为 <code>loggedMethod</code> 返回了一个新函数，因此这个新函数会替换掉 <code>greet</code> 的原始定义。</p>
<p>在 <code>loggedMethod</code> 的定义中带有第二个参数。
它就是上下文对象 <code>context object</code>，包含了一些有关于装饰器声明细节的有用信息 -
例如是否为 <code>#private</code> 成员，或者 <code>static</code>，或者方法的名称。
让我们重写 <code>loggedMethod</code> 来使用这些信息，并且打印出被装饰的方法的名字。</p>
<pre><code class="language-ts">function loggedMethod(
  originalMethod: any,
  context: ClassMethodDecoratorContext
) {
  const methodName = String(context.name);

  function replacementMethod(this: any, ...args: any[]) {
    console.log(`LOG: Entering method '${methodName}'.`);
    const result = originalMethod.call(this, ...args);
    console.log(`LOG: Exiting method '${methodName}'.`);
    return result;
  }

  return replacementMethod;
}
</code></pre>
<p>我们使用了上下文参数。
TypeScript 提供了名为 <code>ClassMethodDecoratorContext</code> 的类型用于描述装饰器方法接收的上下文对象。</p>
<p>除了元数据外，上下文对象中还提供了一个有用的函数 <code>addInitializer</code>。
它提供了一种方式来 hook 到构造函数的起始位置。</p>
<p>例如在 JavaScript 中，下面的情形很常见：</p>
<pre><code class="language-ts">class Person {
  name: string;
  constructor(name: string) {
    this.name = name;

    this.greet = this.greet.bind(this);
  }

  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}
</code></pre>
<p>或者，<code>greet</code> 可以被声明为使用箭头函数初始化的属性。</p>
<pre><code class="language-ts">class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  greet = () =&gt; {
    console.log(`Hello, my name is ${this.name}.`);
  };
}
</code></pre>
<p>这类代码的目的是确保 <code>this</code> 值不会被重新绑定，当 <code>greet</code> 被独立地调用或者在用作回调函数时。</p>
<pre><code class="language-ts">const greet = new Person('Ron').greet;

// 我们不希望下面的调用失败
greet();
</code></pre>
<p>我们可以定义一个装饰器来利用 <code>addInitializer</code> 在构造函数里调用 <code>bind</code>。</p>
<pre><code class="language-ts">function bound(originalMethod: any, context: ClassMethodDecoratorContext) {
  const methodName = context.name;
  if (context.private) {
    throw new Error(
      `'bound' cannot decorate private properties like ${methodName as string}.`
    );
  }
  context.addInitializer(function () {
    this[methodName] = this[methodName].bind(this);
  });
}
</code></pre>
<p><code>bound</code> 没有返回值 - 因此当它装饰一个方法时，不会影响原先的方法。
但是，它会在字段被初始化前添加一些逻辑。</p>
<pre><code class="language-ts">class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  @bound
  @loggedMethod
  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

const p = new Person('Ron');
const greet = p.greet;

// Works!
greet();
</code></pre>
<p>我们将两个装饰器叠在了一起 - <code>@bound</code> 和 <code>@loggedMethod</code>。
这些装饰器以“相反的”顺序执行。
也就是说，<code>@loggedMethod</code> 装饰原始方法 <code>greet</code>，
<code>@bound</code> 装饰的是 <code>@loggedMethod</code> 的结果。
此例中，这不太重要 - 但如果你的装饰器带有副作用或者期望特定的顺序，那就不一样了。</p>
<p>值得注意的是：如果你在乎代码样式，也可以将装饰器放在同一行上。</p>
<pre><code class="language-ts">@bound @loggedMethod greet() {
  console.log(`Hello, my name is ${this.name}.`);
}
</code></pre>
<p>可能不太明显的一点是，你甚至可以定义一个返回装饰器函数的函数。
这样我们可以在一定程序上定制最终的装饰器。
我们可以让 <code>loggedMethod</code> 返回一个装饰器并且定制如何打印消息。</p>
<pre><code class="language-ts">function loggedMethod(headMessage = 'LOG:') {
  return function actualDecorator(
    originalMethod: any,
    context: ClassMethodDecoratorContext
  ) {
    const methodName = String(context.name);

    function replacementMethod(this: any, ...args: any[]) {
      console.log(`${headMessage} Entering method '${methodName}'.`);
      const result = originalMethod.call(this, ...args);
      console.log(`${headMessage} Exiting method '${methodName}'.`);
      return result;
    }

    return replacementMethod;
  };
}
</code></pre>
<p>这样做之后，在使用 <code>loggedMethod</code> 装饰器之前需要先调用它。
接下来就可以传入任意字符串作为打印消息的前缀。</p>
<pre><code class="language-ts">class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  @loggedMethod('')
  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

const p = new Person('Ron');
p.greet();

// Output:
//
//    Entering method 'greet'.
//   Hello, my name is Ron.
//    Exiting method 'greet'.
</code></pre>
<p>装饰器不仅可以用在方法上！
它们也可以被用在属性/字段，存取器（getter/setter）以及自动存取器。
甚至，类本身也可以被装饰，用于处理子类化和注册。</p>
<p>想深入了解装饰器，可以阅读 Axel Rauschmayer 的<a href="https://2ality.com/2022/10/javascript-decorators.html">文章</a>。</p>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/50820">PR</a>。</p>
<h2 id="与旧的实验性的装饰器的差异"><a class="header" href="#与旧的实验性的装饰器的差异">与旧的实验性的装饰器的差异</a></h2>
<p>如果你有一定的 TypeScript 经验，你会发现 TypeScript 多年前就已经支持了“实验性的”装饰器特性。
虽然实验性的装饰器非常地好用，但是它实现的是旧版本的装饰器规范，并且总是需要启用 <code>--experimentalDecorators</code> 编译器选项。
若没有启用它并且使用了装饰器，TypeScript 会报错。</p>
<p>在未来的一段时间内，<code>--experimentalDecorators</code> 依然会存在；
然而，如果不使用该标记，在新代码中装饰器语法也是合法的。
在 <code>--experimentalDecorators</code> 之外，它们的类型检查和代码生成方式也不同。
类型检查和代码生成规则存在巨大差异，以至于虽然装饰器<em>可以</em>被定义为同时支持新、旧装饰器的行为，但任何现有的装饰器函数都不太可能这样做。</p>
<p>新的装饰器提案与 <code>--emitDecoratorMetadata</code> 的实现不兼容，并且不支持在参数上使用装饰器。
未来的 ECMAScript 提案可能会弥补这个差距。</p>
<p>最后要注意的是：除了可以在 <code>export</code> 关键字之前使用装饰器，还可以在 <code>export</code> 或者 <code>export default</code> 之后使用。
但是不允许混合使用两种风格。</p>
<pre><code class="language-ts">//  allowed
@register
export default class Foo {
  // ...
}

//  also allowed
export default
@register
class Bar {
  // ...
}

//  error - before *and* after is not allowed
@before
@after
export class Bar {
  // ...
}
</code></pre>
<h2 id="编写强类型的装饰器"><a class="header" href="#编写强类型的装饰器">编写强类型的装饰器</a></h2>
<p>上面的例子 <code>loggedMethod</code> 和 <code>bound</code> 是故意写的简单并且忽略了大量和类型有关的细节。</p>
<p>为装饰器添加类型可能会很复杂。
例如，强类型的 <code>loggedMethod</code> 可能像下面这样：</p>
<pre><code class="language-ts">function loggedMethod&lt;This, Args extends any[], Return&gt;(
  target: (this: This, ...args: Args) =&gt; Return,
  context: ClassMethodDecoratorContext&lt;
    This,
    (this: This, ...args: Args) =&gt; Return
  &gt;
) {
  const methodName = String(context.name);

  function replacementMethod(this: This, ...args: Args): Return {
    console.log(`LOG: Entering method '${methodName}'.`);
    const result = target.call(this, ...args);
    console.log(`LOG: Exiting method '${methodName}'.`);
    return result;
  }

  return replacementMethod;
}
</code></pre>
<p>我们必须分别给原方法的 <code>this</code>、形式参数和返回值添加类型，上面使用了类型参数 <code>This</code>，<code>Args</code> 以及 <code>Return</code>。
装饰器函数到底有多复杂取决于你要确保什么。
但要记住，装饰器被使用的次数远多于被编写的次数，因此强类型的版本是通常希望得到的 -
但我们需要在可读性之间做出取舍，因此要尽量保持简洁。</p>
<p>未来会有更多关于如何编写装饰器的文档 - 但是<a href="https://2ality.com/2022/10/javascript-decorators.html">这篇文章</a>详细介绍了装饰器的工作方式。</p>
<h2 id="const-类型参数"><a class="header" href="#const-类型参数"><code>const</code> 类型参数</a></h2>
<p>在推断对象类型时，TypeScript 通常会选择一个通用类型。
例如，下例中 <code>names</code> 的推断类型为 <code>string[]</code>：</p>
<pre><code class="language-ts">type HasNames = { readonly names: string[] };
function getNamesExactly&lt;T extends HasNames&gt;(arg: T): T['names'] {
  return arg.names;
}

// Inferred type: string[]
const names = getNamesExactly({ names: ['Alice', 'Bob', 'Eve'] });
</code></pre>
<p>这样做的目的通常是为了允许后面可以进行修改。</p>
<p>然而，根据 <code>getNamesExactly</code> 的具体功能和预期使用方式，通常情况下需要更加具体的类型。</p>
<p>直到现在，API 作者们通常不得不在一些位置上添加 <code>as const</code> 来达到预期的类型推断目的：</p>
<pre><code class="language-ts">// The type we wanted:
//    readonly ["Alice", "Bob", "Eve"]
// The type we got:
//    string[]
const names1 = getNamesExactly({ names: ['Alice', 'Bob', 'Eve'] });

// Correctly gets what we wanted:
//    readonly ["Alice", "Bob", "Eve"]
const names2 = getNamesExactly({ names: ['Alice', 'Bob', 'Eve'] } as const);
</code></pre>
<p>这样做既繁琐又容易忘。
在 TypeScript 5.0 里，你可以为类型参数声明添加 <code>const</code> 修饰符，
这使得 <code>const</code> 形式的类型推断成为默认行为：</p>
<pre><code class="language-ts">type HasNames = { names: readonly string[] };
function getNamesExactly&lt;const T extends HasNames&gt;(arg: T): T['names'] {
  //                       ^^^^^
  return arg.names;
}

// Inferred type: readonly ["Alice", "Bob", "Eve"]
// Note: Didn't need to write 'as const' here
const names = getNamesExactly({ names: ['Alice', 'Bob', 'Eve'] });
</code></pre>
<p>注意，<code>const</code> 修饰符不会<em>拒绝</em>可修改的值，并且不需要不可变约束。
使用可变类型约束可能会产生令人惊讶的结果。</p>
<pre><code class="language-ts">declare function fnBad&lt;const T extends string[]&gt;(args: T): void;

// 'T' is still 'string[]' since 'readonly ["a", "b", "c"]' is not assignable to 'string[]'
fnBad(['a', 'b', 'c']);
</code></pre>
<p>这里，<code>T</code> 的候选推断类型为 <code>readonly ["a", "b", "c"]</code>，但是 <code>readonly</code> 只读数组不能用在需要可变数组的地方。
这种情况下，类型推断会回退到类型约束，将数组视为 <code>string[]</code> 类型，因此函数调用仍然会成功。</p>
<p>这个函数更好的定义是使用 <code>readonly string[]</code>：</p>
<pre><code class="language-ts">declare function fnGood&lt;const T extends readonly string[]&gt;(args: T): void;

// T is readonly ["a", "b", "c"]
fnGood(['a', 'b', 'c']);
</code></pre>
<p>要注意 <code>const</code> 修饰符只影响在函数调用中直接写出的对象、数组和基本表达式的类型推断，
因此，那些无法（或不会）使用 <code>as const</code> 进行修饰的参数在行为上不会有任何变化：</p>
<pre><code class="language-ts">declare function fnGood&lt;const T extends readonly string[]&gt;(args: T): void;
const arr = ['a', 'b', 'c'];

// 'T' is still 'string[]'-- the 'const' modifier has no effect here
fnGood(arr);
</code></pre>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/51865">PR</a>，<a href="https://github.com/microsoft/TypeScript/issues/30680">PR</a> 和 <a href="https://github.com/microsoft/TypeScript/issues/41114">PR</a>。</p>
<h2 id="extends-支持多个配置文件"><a class="header" href="#extends-支持多个配置文件"><code>extends</code> 支持多个配置文件</a></h2>
<p>在管理多个项目时，拥有一个“基础”配置文件，其他 tsconfig.json 文件可以继承它，这会非常有帮助。
这就是为什么 TypeScript 支持使用 <code>extends</code> 字段来从 <code>compilerOptions</code> 中复制字段的原因。</p>
<pre><code class="language-json">// packages/front-end/src/tsconfig.json
{
  "extends": "../../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../lib"
    // ...
  }
}
</code></pre>
<p>然而，有时您可能想要从多个配置文件中进行继承。
例如，假设您正在使用一个<a href="https://github.com/tsconfig/bases">在 npm 上发布的 TypeScript 基础配置文件</a>。
如果您希望自己所有的项目也使用 npm 上的 <code>@tsconfig/strictest</code> 包中的选项，那么有一个简单的解决方案：让 <code>tsconfig.base.json</code> 从 <code>@tsconfig/strictest</code> 进行扩展：</p>
<pre><code class="language-json">// tsconfig.base.json
{
  "extends": "@tsconfig/strictest/tsconfig.json",
  "compilerOptions": {
    // ...
  }
}
</code></pre>
<p>这在某种程度上是有效的。
如果您的某些工程不想使用 <code>@tsconfig/strictest</code>，那么必须手动禁用这些选项，或者创建一个不继承于 <code>@tsconfig/strictest</code> 的 <code>tsconfig.base.json</code>。</p>
<p>为了提高灵活性，TypeScript 5.0 允许 <code>extends</code> 字段指定多个值。
例如，有如下的配置文件：</p>
<pre><code class="language-json">{
  "extends": ["a", "b", "c"],
  "compilerOptions": {
    // ...
  }
}
</code></pre>
<p>这样写就如同是直接继承 <code>c</code>，而 <code>c</code> 继承于 <code>b</code>，<code>b</code> 继承于 <code>a</code>。
如果出现冲突，后来者会被采纳。</p>
<p>在下面的例子中，在最终的 <code>tsconfig.json</code> 中 <code>strictNullChecks</code> 和 <code>noImplicitAny</code> 会被启用。</p>
<pre><code class="language-json">// tsconfig1.json
{
    "compilerOptions": {
        "strictNullChecks": true
    }
}

// tsconfig2.json
{
    "compilerOptions": {
        "noImplicitAny": true
    }
}

// tsconfig.json
{
    "extends": ["./tsconfig1.json", "./tsconfig2.json"],
    "files": ["./index.ts"]
}
</code></pre>
<p>另一个例子，我们可以这样改写最初的示例：</p>
<pre><code class="language-json">// packages/front-end/src/tsconfig.json
{
  "extends": [
    "@tsconfig/strictest/tsconfig.json",
    "../../../tsconfig.base.json"
  ],
  "compilerOptions": {
    "outDir": "../lib"
    // ...
  }
}
</code></pre>
<p>更多详情请参考：<a href="https://github.com/microsoft/TypeScript/pull/50403">PR</a>。</p>
<h2 id="所有的-enum-均为联合-enum"><a class="header" href="#所有的-enum-均为联合-enum">所有的 <code>enum</code> 均为联合 <code>enum</code></a></h2>
<p>在最初 TypeScript 引入枚举类型时，它们只不过是一组同类型的数值常量。</p>
<pre><code class="language-ts">enum E {
  Foo = 10,
  Bar = 20,
}
</code></pre>
<p><code>E.Foo</code> 和 <code>E.Bar</code> 唯一特殊的地方在于它们可以赋值给任何期望类型为 <code>E</code> 的地方。
除此之外，它们基本上等同于 <code>number</code> 类型。</p>
<pre><code class="language-ts">function takeValue(e: E) {}

takeValue(E.Foo); // works
takeValue(123); // error!
</code></pre>
<p>直到 TypeScript 2.0 引入了枚举字面量类型，枚举才变得更为特殊。
枚举字面量类型为每个枚举成员提供了其自己的类型，并将枚举本身转换为每个成员类型的联合类型。
它们还允许我们仅引用枚举中的一部分类型，并细化掉那些类型。</p>
<pre><code class="language-ts">// Color is like a union of Red | Orange | Yellow | Green | Blue | Violet
enum Color {
    Red, Orange, Yellow, Green, Blue, /* Indigo */, Violet
}

// Each enum member has its own type that we can refer to!
type PrimaryColor = Color.Red | Color.Green | Color.Blue;

function isPrimaryColor(c: Color): c is PrimaryColor {
    // Narrowing literal types can catch bugs.
    // TypeScript will error here because
    // we'll end up comparing 'Color.Red' to 'Color.Green'.
    // We meant to use ||, but accidentally wrote &amp;&amp;.
    return c === Color.Red &amp;&amp; c === Color.Green &amp;&amp; c === Color.Blue;
}
</code></pre>
<p>为每个枚举成员提供其自己的类型的一个问题是，这些类型在某种程度上与成员的实际值相关联。
在某些情况下，无法计算该值 - 例如，枚举成员可能由函数调用初始化。</p>
<pre><code class="language-ts">enum E {
  Blah = Math.random(),
}
</code></pre>
<p>每当 TypeScript 遇到这些问题时，它会悄悄地退而使用旧的枚举策略。
这意味着放弃所有联合类型和字面量类型的优势。</p>
<p>TypeScript 5.0 通过为每个计算成员创建唯一类型，成功将所有枚举转换为联合枚举。
这意味着现在所有枚举都可以被细化，并且每个枚举成员都有其自己的类型。</p>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/50528">PR</a></p>
<h2 id="--moduleresolution-bundler"><a class="header" href="#--moduleresolution-bundler"><code>--moduleResolution bundler</code></a></h2>
<p>TypeScript 4.7 支持将 <code>--module</code> 和 <code>--moduleResolution</code> 选项设置为 <code>node16</code> 和 <code>nodenext</code>。
这些选项的目的是更好地模拟 <code>Node.js</code> 中 ECMAScript 模块的精确查找规则；
然而，这种模式存在许多其他工具实际上并不强制执行的限制。</p>
<p>例如，在 Node.js 的 ECMAScript 模块中，任何相对导入都需要包含文件扩展名。</p>
<pre><code class="language-ts">// entry.mjs
import * as utils from './utils'; //  wrong - we need to include the file extension.

import * as utils from './utils.mjs'; //  works
</code></pre>
<p>对于 Node.js 和浏览器来说，这样做有一些原因 - 它可以加快文件查找速度，并且对于简单的文件服务器效果更好。
但是对于许多使用打包工具的开发人员来说，<code>node16</code> / <code>nodenext</code> 设置很麻烦，
因为打包工具中没有这么多限制。
在某些方面，<code>node</code> 解析模式对于任何使用打包工具的人来说是更好的。</p>
<p>但在某些方面，原始的 <code>node</code> 解析模式已经过时了。
大多数现代打包工具在 Node.js 中使用 ECMAScript 模块和 CommonJS 查找规则的融合。
例如，像在 CommonJS 中一样，无扩展名的导入也可以正常工作，但是在查找<a href="https://nodejs.org/api/packages.html#nested-conditions">包的导出条件</a>时，它们将首选像在 ECMAScript 文件中一样的 <code>import</code> 条件。</p>
<p>为了模拟打包工具的工作方式，TypeScript 现在引入了一种新策略：<code>--moduleResolution bundler</code>。</p>
<pre><code class="language-json">{
  "compilerOptions": {
    "target": "esnext",
    "moduleResolution": "bundler"
  }
}
</code></pre>
<p>如果你使用如 Vite， esbuild, swc, Webpack, parcel 等现代打包工具，它们实现了混合的查找策略，新的 <code>bundler</code> 选项是更好的选择。</p>
<p>另一方面，如果您正在编写一个要发布到 npm 的代码库，那么使用 <code>bundler</code> 选项可能会隐藏影响未使用打包工具用户的兼容性问题。
因此，在这些情况下，使用 <code>node16</code> 或 <code>nodenext</code> 解析选项可能是更好的选择。</p>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/51669">PR</a></p>
<h2 id="定制化解析的标记"><a class="header" href="#定制化解析的标记">定制化解析的标记</a></h2>
<p>JavaScript 工具现在可以模拟“混合”解析规则，就像我们上面描述的 <code>bundler</code> 模式一样。
由于工具的支持可能有所不同，因此 TypeScript 5.0 提供了启用或禁用一些功能的方法，这些功能可能无法与您的配置一起使用。</p>
<h3 id="allowimportingtsextensions"><a class="header" href="#allowimportingtsextensions"><code>allowImportingTsExtensions</code></a></h3>
<p><code>--allowImportingTsExtensions</code> 允许 TypeScript 文件导入使用了 TypeScript 特定扩展名的文件，例如 <code>.ts</code>, <code>.mts</code>, <code>.tsx</code>。</p>
<p>此标记仅在启用了 <code>--noEmit</code> 或 <code>--emitDeclarationOnly</code> 时允许使用，
因为这些导入路径无法在运行时的 JavaScript 输出文件中被解析。
这里的期望是，您的解析器（例如打包工具、运行时或其他工具）将保证这些在 <code>.ts</code> 文件之间的导入可以工作。</p>
<h3 id="resolvepackagejsonexports"><a class="header" href="#resolvepackagejsonexports">resolvePackageJsonExports</a></h3>
<p><code>--resolvePackageJsonExports</code> 强制 TypeScript 使用 <a href="https://nodejs.org/api/packages.html#exports">package.json 里的 exports 字段</a>，如果它尝试读取 <code>node_modules</code> 里的某个包。</p>
<p>当 <code>--moduleResolution</code> 为 <code>node16</code>, <code>nodenext</code> 和 <code>bundler</code> 时，该选项的默认值为 <code>true</code>。</p>
<h3 id="resolvepackagejsonimports"><a class="header" href="#resolvepackagejsonimports"><code>resolvePackageJsonImports</code></a></h3>
<p><code>--resolvePackageJsonImports</code> 强制 TypeScript 使用 <a href="https://nodejs.org/api/packages.html#imports">package.json 里的 imports 字段</a>，当它查找以 <code>#</code> 开头的文件时，且该文件的父目录中包含 <code>package.json</code> 文件。</p>
<p>当 <code>--moduleResolution</code> 为 <code>node16</code>, <code>nodenext</code> 和 <code>bundler</code> 时，该选项的默认值为 <code>true</code>。</p>
<h3 id="allowarbitraryextensions"><a class="header" href="#allowarbitraryextensions"><code>allowArbitraryExtensions</code></a></h3>
<p>在 TypeScript 5.0 中，当导入路径不是以已知的 JavaScript 或 TypeScript 文件扩展名结尾时，编译器将查找该路径的声明文件，形式为 <code>{文件基础名称}.d.{扩展名}.ts</code>。
例如，如果您在打包项目中使用 CSS 加载器，您可能需要编写（或生成）如下的声明文件：</p>
<pre><code class="language-css">/* app.css */
.cookie-banner {
  display: none;
}
</code></pre>
<pre><code class="language-ts">// app.d.css.ts
declare const css: {
  cookieBanner: string;
};
export default css;
</code></pre>
<pre><code class="language-tsx">// App.tsx
import styles from './app.css';

styles.cookieBanner; // string
</code></pre>
<p>默认情况下，该导入将引发错误，告诉您 TypeScript 不支持此文件类型，您的运行时可能不支持导入它。
但是，如果您已经配置了运行时或打包工具来处理它，您可以使用新的 <code>--allowArbitraryExtensions</code> 编译器选项来抑制错误。</p>
<p>需要注意的是，历史上通常可以通过添加名为 <code>app.css.d.ts</code> 而不是 <code>app.d.css.ts</code> 的声明文件来实现类似的效果 - 但是，这只在 Node.js 中 CommonJS 的 <code>require</code> 解析规则下可以工作。
严格来说，前者被解析为名为 <code>app.css.js</code> 的 JavaScript 文件的声明文件。
由于 Node 中的 ESM 需要使用包含扩展名的相对文件导入，因此在 <code>--moduleResolution</code> 为 <code>node16</code> 或 <code>nodenext</code> 时，TypeScript 会在示例的 ESM 文件中报错。</p>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/issues/50133">PR</a> <a href="https://github.com/microsoft/TypeScript/pull/51435">PR</a>。</p>
<h3 id="customconditions"><a class="header" href="#customconditions"><code>customConditions</code></a></h3>
<p><code>--customConditions</code> 接受额外的<a href="https://nodejs.org/api/packages.html#nested-conditions">条件</a>列表，当 TypeScript 从 package.json 的<a href="https://nodejs.org/api/packages.html#exports">exports</a>或 <a href="https://nodejs.org/api/packages.html#imports">imports</a> 字段解析时，这些条件应该成功。
这些条件会被添加到解析器默认使用的任何现有条件中。</p>
<p>例如，有如下的配置：</p>
<pre><code class="language-json">{
  "compilerOptions": {
    "target": "es2022",
    "moduleResolution": "bundler",
    "customConditions": ["my-condition"]
  }
}
</code></pre>
<p>每当 <code>package.json</code> 里引用了 <code>exports</code> 或 <code>imports</code> 字段时，TypeScript 都会考虑名为 <code>my-condition</code> 的条件。</p>
<p>所以当从具有如下 <code>package.json</code> 的包中导入时：</p>
<pre><code class="language-json">{
  // ...
  "exports": {
    ".": {
      "my-condition": "./foo.mjs",
      "node": "./bar.mjs",
      "import": "./baz.mjs",
      "require": "./biz.mjs"
    }
  }
}
</code></pre>
<p>TypeScript 会尝试查找 <code>foo.mjs</code> 文件。</p>
<p>该字段仅在 <code>--moduleResolution</code> 为 <code>node16</code>, <code>nodenext</code> 和 <code>bundler</code> 时有效。</p>
<h2 id="--verbatimmodulesyntax"><a class="header" href="#--verbatimmodulesyntax">--verbatimModuleSyntax</a></h2>
<p>在默认情况下，TypeScript 会执行<em>导入省略</em>。
大体上来讲，如果有如下代码：</p>
<pre><code class="language-ts">import { Car } from './car';

export function drive(car: Car) {
  // ...
}
</code></pre>
<p>TypeScript 能够检测到导入语句仅用于导入类型，因此会删除导入语句。
最终生成的 JavaScript 代码如下：</p>
<pre><code class="language-js">export function drive(car) {
  // ...
}
</code></pre>
<p>大多数情况下这是没问题的，因为如果 <code>Car</code> 不是从 <code>./car</code> 导出的值，我们将会得到一个运行时错误。</p>
<p>但在一些特殊情况下，它增加了一层复杂性。
例如，不存在像 <code>import "./car";</code> 这样的语句 - 这个导入语句会被完全删除。
这对于有副作用的模块来讲是有区别的。</p>
<p>TypeScript 的 JavaScript 代码生成策略还有其它一些复杂性 - 导入省略不仅只是由导入语句的使用方式决定 - 它还取决于值的声明方式。
因此，如下的代码的处理方式不总是那么明显：</p>
<pre><code class="language-ts">export { Car } from './car';
</code></pre>
<p>这段代码是应该保留还是删除？
如果 <code>Car</code> 是使用 <code>class</code> 声明的，那么在生成的 JavaScript 代码中会被保留。
但是如果 <code>Car</code> 是使用类型别名或 <code>interface</code> 声明的，那么在生成的 JavaScript 代码中会被省略。</p>
<p>尽管 TypeScript 可以根据多个文件来综合判断如何生成代码，但不是所有的编译器都能够做到。</p>
<p>导入和导出语句中的 <code>type</code> 修饰符能够起到一点作用。
我们可以使用 <code>type</code> 修饰符明确声明导入和导出是否仅用于类型分析，并且可以在生成的 JavaScript 文件中完全删除。</p>
<pre><code class="language-ts">// This statement can be dropped entirely in JS output
import type * as car from './car';

// The named import/export 'Car' can be dropped in JS output
import { type Car } from './car';
export { type Car } from './car';
</code></pre>
<p><code>type</code> 修饰符本身并不是特别管用 - 默认情况下，导入省略仍会删除导入语句，
并且不强制要求您区分类型导入和普通导入以及导出。
因此，TypeScript 提供了 <code>--importsNotUsedAsValues</code> 来确保您使用类型修饰符，
<code>--preserveValueImports</code> 来防止<em>某些</em>模块消除行为，
以及 <code>--isolatedModules</code> 来确保您的 TypeScript 代码在不同编译器中都能正常运行。
不幸的是，理解这三个标志的细节很困难，并且仍然存在一些意外行为的边缘情况。</p>
<p>TypeScript 5.0 提供了一个新的 <code>--verbatimModuleSyntax</code> 来简化这个情况。
规则很简单 - 所有不带 <code>type</code> 修饰符的导入导出语句会被保留。
任何带有 <code>type</code> 修饰符的导入导出语句会被删除。</p>
<pre><code class="language-ts">// Erased away entirely.
import type { A } from 'a';

// Rewritten to 'import { b } from "bcd";'
import { b, type c, type d } from 'bcd';

// Rewritten to 'import {} from "xyz";'
import { type xyz } from 'xyz';
</code></pre>
<p>使用这个新的选项，实现了所见即所得。</p>
<p>但是，这在涉及模块互操作性时会有一些影响。
在这个标志下，当您的设置或文件扩展名暗示了不同的模块系统时，ECMAScript 的导入和导出不会被重写为 <code>require</code> 调用。
相反，您会收到一个错误。
如果您需要生成使用 <code>require</code> 和 <code>module.exports</code> 的代码，您需要使用早于 ES2015 的 TypeScript 的模块语法：</p>
<pre><code class="language-ts">import foo = require('foo');

// ==&gt;

const foo = require('foo');
</code></pre>
<pre><code class="language-ts">function foo() {}
function bar() {}
function baz() {}

export = {
  foo,
  bar,
  baz,
};

// ==&gt;

function foo() {}
function bar() {}
function baz() {}

module.exports = {
  foo,
  bar,
  baz,
};
</code></pre>
<p>虽然这是一种限制，但它确实有助于使一些问题更加明显。
例如，在 <code>--module node16</code> 下很容易忘记<a href="https://nodejs.org/api/packages.html#type">在 package.json 中设置 <code>type</code> 字段</a>。
结果是开发人员会开始编写 CommonJS 模块而不是 ES 模块，但却没有意识到这一点，从而导致查找规则和 JavaScript 输出出现意外的结果。
这个新的标志确保您有意识地使用文件类型，因为语法是刻意不同的。</p>
<p>因为 <code>--verbatimModuleSyntax</code> 相比于 <code>--importsNotUsedAsValues</code> 和 <code>--preserveValueImports</code> 提供了更加一致的行为，推荐使用前者，后两个标记将被弃用。</p>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/52203">PR</a> 和 <a href="https://github.com/microsoft/TypeScript/issues/51479">issue</a>.</p>
<h2 id="支持-export-type-"><a class="header" href="#支持-export-type-">支持 <code>export type *</code></a></h2>
<p>在 TypeScript 3.8 引入类型导入时，该语法不支持在 <code>export * from "module"</code> 或 <code>export * as ns from "module"</code> 重新导出上使用。
TypeScript 5.0 添加了对两者的支持：</p>
<pre><code class="language-ts">// models/vehicles.ts
export class Spaceship {
  // ...
}

// models/index.ts
export type * as vehicles from './vehicles';

// main.ts
import { vehicles } from './models';

function takeASpaceship(s: vehicles.Spaceship) {
  //  ok - `vehicles` only used in a type position
}

function makeASpaceship() {
  return new vehicles.Spaceship();
  //         ^^^^^^^^
  // 'vehicles' cannot be used as a value because it was exported using 'export type'.
}
</code></pre>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/52217">PR</a>。</p>
<h2 id="支持-jsdoc-中的-satisfies"><a class="header" href="#支持-jsdoc-中的-satisfies">支持 JSDoc 中的 <code>@satisfies</code></a></h2>
<p>TypeScript 4.9 支持 <code>satisfies</code> 运算符。
它确保了表达式的类型是兼容的，且不影响类型自身。
例如，有如下代码：</p>
<pre><code class="language-ts">interface CompilerOptions {
  strict?: boolean;
  outDir?: string;
  // ...
}

interface ConfigSettings {
  compilerOptions?: CompilerOptions;
  extends?: string | string[];
  // ...
}

let myConfigSettings = {
  compilerOptions: {
    strict: true,
    outDir: '../lib',
    // ...
  },

  extends: ['@tsconfig/strictest/tsconfig.json', '../../../tsconfig.base.json'],
} satisfies ConfigSettings;
</code></pre>
<p>这里，TypeScript 知道 <code>myConfigSettings.extends</code> 声明为数组 - 因为 <code>satisfies</code> 会验证对象的类型。
因此，如果我们想在 <code>extends</code> 上进行映射操作，那是可以的。</p>
<pre><code class="language-ts">declare function resolveConfig(configPath: string): CompilerOptions;

let inheritedConfigs = myConfigSettings.extends.map(resolveConfig);
</code></pre>
<p>这对 TypeScript 用户来讲是有用处的，但是许多人使用 TypeScript 来对带有 JSDoc 的 JavaScript 代码进行类型检查。
因此，TypeScript 5.0 支持了新的 JSDoc 标签 <code>@satisfies</code> 来做相同的事。</p>
<p><code>/** @satisfies */</code> 能够检查出类型不匹配：</p>
<pre><code class="language-ts">// @ts-check

/**
 * @typedef CompilerOptions
 * @prop {boolean} [strict]
 * @prop {string} [outDir]
 */

/**
 * @satisfies {CompilerOptions}
 */
let myCompilerOptions = {
  outdir: '../lib',
  //  ~~~~~~ oops! we meant outDir
};
</code></pre>
<p>但它会保留表达式的原始类型，允许我们稍后使用值的更详细的类型。</p>
<pre><code class="language-ts">// @ts-check

/**
 * @typedef CompilerOptions
 * @prop {boolean} [strict]
 * @prop {string} [outDir]
 */

/**
 * @typedef ConfigSettings
 * @prop {CompilerOptions} [compilerOptions]
 * @prop {string | string[]} [extends]
 */

/**
 * @satisfies {ConfigSettings}
 */
let myConfigSettings = {
  compilerOptions: {
    strict: true,
    outDir: '../lib',
  },
  extends: ['@tsconfig/strictest/tsconfig.json', '../../../tsconfig.base.json'],
};

let inheritedConfigs = myConfigSettings.extends.map(resolveConfig);
</code></pre>
<p><code>/** @satisfies */</code> 也可以在行内的括号表达式上使用。
可以像下面这样定义 <code>myConfigSettings</code>：</p>
<pre><code class="language-ts">let myConfigSettings = /** @satisfies {ConfigSettings} */ {
  compilerOptions: {
    strict: true,
    outDir: '../lib',
  },
  extends: ['@tsconfig/strictest/tsconfig.json', '../../../tsconfig.base.json'],
};
</code></pre>
<p>为什么？当你更深入地研究其他代码时，比如函数调用，它通常更有意义。</p>
<pre><code class="language-ts">compileCode(
  /** @satisfies {ConfigSettings} */ {
    // ...
  }
);
</code></pre>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/51753">PR</a>。
感谢作者 <a href="https://github.com/a-tarasyuk">Oleksandr Tarasiuk</a>。</p>
<h2 id="支持-jsdoc-中的-overload"><a class="header" href="#支持-jsdoc-中的-overload">支持 JSDoc 中的 <code>@overload</code></a></h2>
<p>在 TypeScript 中，你可以为一个函数指定多个重载。
使用重载能够描述一个函数可以使用不同的参数进行调用，也可能会返回不同的结果。
它们可以限制调用方如何调用函数，并细化他们将得到的结果。</p>
<pre><code class="language-ts">// Our overloads:
function printValue(str: string): void;
function printValue(num: number, maxFractionDigits?: number): void;

// Our implementation:
function printValue(value: string | number, maximumFractionDigits?: number) {
  if (typeof value === 'number') {
    const formatter = Intl.NumberFormat('en-US', {
      maximumFractionDigits,
    });
    value = formatter.format(value);
  }

  console.log(value);
}
</code></pre>
<p>这里表示 <code>printValue</code> 的第一个参数可以为 <code>string</code> 或 <code>number</code> 类型。
如果接收的是 <code>number</code> 类型，那么它还接收第二个参数决定打印的小数位数。</p>
<p>TypeScript 5.0 支持在 JSDoc 里使用 <code>@overload</code> 来声明重载。
每一个 JSDoc <code>@overload</code> 标记都表示一个不同的函数重载。</p>
<pre><code class="language-js">// @ts-check

/**
 * @overload
 * @param {string} value
 * @return {void}
 */

/**
 * @overload
 * @param {number} value
 * @param {number} [maximumFractionDigits]
 * @return {void}
 */

/**
 * @param {string | number} value
 * @param {number} [maximumFractionDigits]
 */
function printValue(value, maximumFractionDigits) {
  if (typeof value === 'number') {
    const formatter = Intl.NumberFormat('en-US', {
      maximumFractionDigits,
    });
    value = formatter.format(value);
  }

  console.log(value);
}
</code></pre>
<p>现在不论是编写 TypeScript 文件还是 JavaScript 文件，TypeScript 都能够提示函数调用是否正确。</p>
<pre><code class="language-js">// all allowed
printValue('hello!');
printValue(123.45);
printValue(123.45, 2);

printValue('hello!', 123); // error!
</code></pre>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/51234">PR</a>，感谢 <a href="https://github.com/apendua">Tomasz Lenarcik</a>。</p>
<h2 id="在---build-模式下使用有关文件生成的选项"><a class="header" href="#在---build-模式下使用有关文件生成的选项">在 <code>--build</code> 模式下使用有关文件生成的选项</a></h2>
<p>TypeScript 现在允许在 <code>--build</code> 模式下使用如下选项：</p>
<ul>
<li><code>--declaration</code></li>
<li><code>--emitDeclarationOnly</code></li>
<li><code>--declarationMap</code></li>
<li><code>--sourceMap</code></li>
<li><code>--inlineSourceMap</code></li>
</ul>
<p>这使得在构建过程中定制某些部分变得更加容易，特别是在你可能会有不同的开发和生产构建时。</p>
<p>例如，一个库的开发构建可能不需要生成声明文件，但是生产构建则需要。
一个项目可以将生成声明文件配置为默认关闭，并使用如下方式构建：</p>
<pre><code class="language-sh">tsc --build -p ./my-project-dir
</code></pre>
<p>开发完毕后，在“生产环境”构建时使用 <code>--declaration</code> 选项：</p>
<pre><code class="language-sh">tsc --build -p ./my-project-dir --declaration
</code></pre>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/51241">PR</a>。</p>
<h2 id="编辑器导入语句排序时不区分大小写"><a class="header" href="#编辑器导入语句排序时不区分大小写">编辑器导入语句排序时不区分大小写</a></h2>
<p>在 Visual Studio 和 VS Code 等编辑器中，TypeScript 可以帮助组织和排序导入和导出语句。
不过，通常情况下，对于何时将列表“排序”，可能会有不同的解释。</p>
<p>例如，下面的导入列表是否已排序？</p>
<pre><code class="language-ts">import { Toggle, freeze, toBoolean } from './utils';
</code></pre>
<p>令人惊讶的是，答案可能是“这要看情况”。
如果我们不考虑大小写敏感性，那么这个列表显然是没有排序的。
字母<code>f</code>排在<code>t</code>和<code>T</code>之前。</p>
<p>但在大多数编程语言中，排序默认是比较字符串的字节值。
JavaScript 比较字符串的方式意味着 “Toggle” 总是排在 “freeze” 之前，因为根据 <a href="https://en.wikipedia.org/wiki/ASCII">ASCII 字符编码</a>，大写字母排在小写字母之前。
所以从这个角度来看，导入列表是已排序的。</p>
<p>以前，TypeScript 认为导入列表已排序，因为它进行了基本的大小写敏感排序。
这可能让开发人员感到沮丧，因为他们更喜欢不区分大小写的排序方式，或者使用像 ESLint 这样的工具默认需要不区分大小写的排序方式。</p>
<p>现在，TypeScript 默认会检测大小写敏感性。
这意味着 TypeScript 和类似 ESLint 的工具通常不会因为如何最好地排序导入而“互相冲突”。</p>
<p>我们的团队还在尝试更多的排序策略，你可以在<a href="https://github.com/microsoft/TypeScript/pull/52115">这里了解更多</a>。
这些选项可能最终可以由编辑器进行配置。
目前，它们仍然不稳定和实验性的，你可以通过在 JSON 选项中使用 typescript.unstable 条目来选择它们。
下面是你可以尝试的所有选项（设置为它们的默认值）：</p>
<pre><code class="language-json">{
  "typescript.unstable": {
    // Should sorting be case-sensitive? Can be:
    // - true
    // - false
    // - "auto" (auto-detect)
    "organizeImportsIgnoreCase": "auto",

    // Should sorting be "ordinal" and use code points or consider Unicode rules? Can be:
    // - "ordinal"
    // - "unicode"
    "organizeImportsCollation": "ordinal",

    // Under `"organizeImportsCollation": "unicode"`,
    // what is the current locale? Can be:
    // - [any other locale code]
    // - "auto" (use the editor's locale)
    "organizeImportsLocale": "en",

    // Under `"organizeImportsCollation": "unicode"`,
    // should upper-case letters or lower-case letters come first? Can be:
    // - false (locale-specific)
    // - "upper"
    // - "lower"
    "organizeImportsCaseFirst": false,

    // Under `"organizeImportsCollation": "unicode"`,
    // do runs of numbers get compared numerically (i.e. "a1" &lt; "a2" &lt; "a100")? Can be:
    // - true
    // - false
    "organizeImportsNumericCollation": true,

    // Under `"organizeImportsCollation": "unicode"`,
    // do letters with accent marks/diacritics get sorted distinctly
    // from their "base" letter (i.e. is é different from e)? Can be
    // - true
    // - false
    "organizeImportsAccentCollation": true
  },
  "javascript.unstable": {
    // same options valid here...
  }
}
</code></pre>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/51733">PR</a> 和 <a href="https://github.com/microsoft/TypeScript/pull/52115">PR</a>。</p>
<h2 id="穷举式-switchcase-自动补全"><a class="header" href="#穷举式-switchcase-自动补全">穷举式 <code>switch/case</code> 自动补全</a></h2>
<p>在编写 <code>switch</code> 语句时，TypeScript 现在会检测被检查的值是否具有字面量类型。
如果是，它将提供一个补全选项，可以为每个未覆盖的情况构建骨架代码。</p>
<p>更多详情请参考 <a href="https://github.com/microsoft/TypeScript/pull/50996">PR</a>。</p>
<h2 id="速度内存以及代码包尺寸优化"><a class="header" href="#速度内存以及代码包尺寸优化">速度，内存以及代码包尺寸优化</a></h2>
<p>TypeScript 5.0 在我们的代码结构、数据结构和算法实现方面进行了许多强大的变化。
这些变化的意义在于，整个体验都应该更快 —— 不仅仅是运行 TypeScript，甚至包括安装 TypeScript。</p>
<p>以下是我们相对于 TypeScript 4.9 能够获得的一些有趣的速度和大小优势。</p>
<div class="table-wrapper"><table><thead><tr><th>Scenario</th><th>Time or Size Relative to TS 4.9</th></tr></thead><tbody>
<tr><td>material-ui build time</td><td>90%</td></tr>
<tr><td>TypeScript Compiler startup time</td><td>89%</td></tr>
<tr><td>Playwright build time</td><td>88%</td></tr>
<tr><td>TypeScript Compiler self-build time</td><td>87%</td></tr>
<tr><td>Outlook Web build time</td><td>82%</td></tr>
<tr><td>VS Code build time</td><td>80%</td></tr>
<tr><td>typescript npm Package Size</td><td>59%</td></tr>
</tbody></table>
</div>
<p><img src="https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2023/03/speed-5.0-stable-2.png" alt="img" /></p>
<p><img src="https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2023/03/size-5.0-stable-1.png" alt="img" /></p>
<p>怎么做到的呢？我们将在未来的博客文章中详细介绍一些值得注意的改进。
但我们不会让你等到那篇博客文章。</p>
<p>首先，我们最近将 TypeScript 从命名空间迁移到了模块，这使我们能够利用现代构建工具来执行像作用域提升这样的优化。
使用这些工具，重新审视我们的打包策略，并删除一些已过时的代码，使 TypeScript 4.9 的 63.8 MB 包大小减少了约 26.4 MB。
这也通过直接函数调用为我们带来了显著的加速。
我们在这里撰写了关于我们迁移到模块的<a href="https://devblogs.microsoft.com/typescript/typescripts-migration-to-modules/">详细介绍</a>。</p>
<p>TypeScript 还在编译器内部对象类型上增加了更多的一致性，并且也减少了一些这些对象类型上存储的数据。
这减少了多态操作，同时平衡了由于使我们的对象结构更加一致而带来的内存使用增加。</p>
<p>我们还在将信息序列化为字符串时执行了一些缓存。
类型显示，它可能在错误报告、声明生成、代码补全等情况下使用，是非常昂贵的操作。
TypeScript 现在对一些常用的机制进行缓存，以便在这些操作之间重复使用。</p>
<p>我们进行了一个值得注意的改变，改善了我们的解析器，即在某些情况下，利用 var 来避免在闭包中使用 let 和 const 的成本。
这提高了一些解析性能。</p>
<p>总的来说，我们预计大多数代码库应该会从 TypeScript 5.0 中看到速度的提升，并且一直能够保持 10% 到 20% 之间的优势。
当然，这将取决于硬件和代码库的特性，但我们鼓励你今天就在你的代码库上尝试它！</p>
<p>更多详情：</p>
<ul>
<li><a href="https://github.com/microsoft/TypeScript/pull/51387">Migrate to Modules</a></li>
<li><a href="https://github.com/microsoft/TypeScript/pull/51682">Node Monomorphization</a></li>
<li><a href="https://github.com/microsoft/TypeScript/pull/51880">Symbol Monomorphization</a></li>
<li><a href="https://github.com/microsoft/TypeScript/pull/52170">Identifier Size Reduction</a></li>
<li><a href="https://github.com/microsoft/TypeScript/pull/52382">Printer Caching</a></li>
<li><a href="https://github.com/microsoft/TypeScript/issues/52924">Limited Usage of var</a></li>
</ul>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../../zh/release-notes/typescript-5.1.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>

                            <a rel="next prefetch" href="../../zh/release-notes/typescript-4.9.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>

                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../../zh/release-notes/typescript-5.1.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

                    <a rel="next prefetch" href="../../zh/release-notes/typescript-4.9.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>




        <script>
            window.playground_copyable = true;
        </script>


        <script src="../../elasticlunr.min.js"></script>
        <script src="../../mark.min.js"></script>
        <script src="../../searcher.js"></script>

        <script src="../../clipboard.min.js"></script>
        <script src="../../highlight.js"></script>
        <script src="../../book.js"></script>

        <!-- Custom JS scripts -->


    </div>
    </body>
</html>
